<template>
  <view class="container">
    <headerVue title="首页" active="首页"></headerVue>
    <view class="box-1" :style="{marginTop:systemBarHeight+'px'}">
      <scroll-view :scroll-top="scrollTop" :show-scrollbar="true" scroll-y refresher-background="transparent"
                   style="height: 100%;" @refresherrefresh="refresherrefresh" :refresher-enabled="true"
                   :scroll-with-animation="true" :refresher-triggered="scrollView.refresherTriggered">
        <view class="talk-list" :style="{paddingBottom:paddingBottom+'px'}">
          <view v-for="(item,index) in talkList" :key="item.id" :id="'msg-'+item.id">
            <view class="item flex-col" :class=" item.type == 1 ? 'push':'pull' ">
              <!-- <image :src="item.pic" mode="aspectFill" class="pic"></image> -->
              <view class="">
                <view class="content">
                  <template v-if="item.contentType === 'image'">
                    <image :src="item.content" mode="widthFix" style="width: 400rpx;"></image>
                  </template>
                  <template v-else>
                    <view class="text">{{ item.content }}</view>
                    <view v-if="index===firstPull" class="order-buts">
                      <view class="buts-left">
                        <!-- 觉得回答不好 -->
                        <view @click.stop="showNoGood=true" class="_item" hover-class="yun-hover">
                          <u-icon color="#716AE5" size="40" name="thumb-down"></u-icon>
                        </view>
                        <!-- 播放语音 -->
                        <view class="_item" @click.stop="handlePlay(item)" hover-class="yun-hover">
                          <view v-if="isPlay" class="voice-playing" hover-class="yun-hover">
                            <view class="play1"></view>
                            <view class="play2"></view>
                            <view class="play3"></view>
                          </view>
                          <u-icon v-else @click.stop="cancelPlay" color="#716AE5" size="40" name="volume"></u-icon>
                        </view>
                      </view>
                      <!-- 刷新回答 -->
                      <view class="buts-right">
                        <!-- <text class="iconfont icon-nogood"></text> -->
                        <view class="_item" hover-class="yun-hover">
                          <u-icon color="#716AE5" size="40" name="reload"></u-icon>
                        </view>
                      </view>
                    </view>
                  </template>
                </view>
                <!-- 								<view class="_btns">
                  <u-icon @click.stop="handleVolume(index)" size="36"
                    :name="item.isVolume?'volume-fill':'volume'" />
                </view> -->
              </view>
            </view>
          </view>
        </view>
        <!-- 不能删 为了能滚动到底部的 -->
        <view id="scroll"></view>
      </scroll-view>
    </view>
    <view class="footer">
      <!--   v-if="showMessage"   -->
      <view @click="handleMessage" class="message">
        <u--image :showLoading="true" src="/static/icon-img/icon-message.png" width="50rpx" height="50rpx"/>
      </view>
      <view v-if="isMic" class="talk-container">
        <view class="voiceText">
          {{ voiceText || '点击按钮开始说话' }}
          <view v-if="showError" class="error-tip">{{ errorMessage }}</view>
        </view>

        <view
            class="talk"
            :class="{ recording: isRecording }"
            @touchstart="handleTouchStart"
            @touchmove="handleTouchMove"
            @touchend="handleTouchEnd"
            @touchcancel="handleTouchCancel"
        >
          {{ isRecording ? '松开结束' : '按住说话' }}
        </view>
      </view>
      <u--textarea @linechange="linechange" :confirmType="null" :cursorSpacing="20" v-else autoHeight type="text"
                   class="content"
                   v-model="content" placeholder="有什么问题尽管吩咐~" :maxlength='-1' @blur="contentBlur"
                   @focus="contentFocus"/>
      <view class="message send">
        <u-icon v-if="content" @click.stop="handleSendClick" name="arrow-upward" color="#2979ff" size="50"></u-icon>
        <template v-else>
          <u-icon @click="isMic=true" v-if="!isMic" size="40" name="/static/icon-svg/mic.svg"></u-icon>
          <u-icon @click="handleKeyboard" v-else size="44" name="/static/icon-svg/keyboard.svg"></u-icon>
        </template>
      </view>
    </view>
    <u-no-network/>
    <!-- 录音提示层 -->
    <view v-if="showRecordPanel" class="record-mask">
      <view class="record-content">
        <view class="record-time">{{ recordTime }}s</view>
        <view class="record-animation">
          <view class="wave"></view>
          <u-icon name="mic" color="#fff" size="80"></u-icon>
        </view>
        <view :class="['cancel-tip', { active: isCancelled }]">
          {{ isCancelled ? '松开取消' : '松开发送，上滑取消' }}
        </view>
        <view v-if="isCancelled" class="cancel-icon">
          <u-icon name="close-circle" color="#ff4444" size="120"></u-icon>
        </view>
      </view>
    </view>
    <!-- 热词 -->
    <popupHotWordVue :show.sync="showHotWord"/>
    <!-- 消息反馈 -->
    <popupNoGood :show.sync="showNoGood"/>
  </view>
</template>
<script>
import {
  mapGetters
} from 'vuex';
import {
  getHistoryMsg
} from "@/request/template-talk/history-msg.js";

import headerVue from '@/pages/header/header.vue';
import popupHotWordVue from './components/popup-hot-word.vue';
import popupNoGood from './components/popup-no-good.vue'

// 录音播放事件引入
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
let plugin = null
let manager = null
/*#ifdef MP*/
plugin = requirePlugin("WechatSI");
manager = plugin.getRecordRecognitionManager();
/*#endif*/
export default {
  components: {
    headerVue,
    popupHotWordVue,
    popupNoGood
  },
  data() {
    return {
      // 滚动容器
      scrollView: {
        refresherTriggered: false,
        intoView: "",
        safeAreaHeight: 0
      },
      // 聊天列表数据
      talkList: [],
      // 请求参数
      ajax: {
        rows: 20, //每页数量
        page: 1, //页码
        flag: true, // 请求开关
      },
      // 发送内容
      content: '',
      focusStatus: false,
      showHotWord: false,
      showNoGood: false,
      isMic: false,
      isRecording: false,
      pillarHeights: Array(7).fill(0),
      voicePath: '',
      voiceText: "",
      shows: false,
      isPlay: false,
      mainHeight: 0,
      scrollTop: 0,
      showRecordPanel: false,
      isCancelled: false,
      recordTime: 0,
      maxRecordTime: 60,
      timer: null,
      startY: 0,
      showError: false,
      errorMessage: '',
    }
  },
  computed: {
    ...mapGetters('plugins', ['systemBarHeight']),
    showMessage() {
      return !this.focusStatus && !this.content
    },
    paddingBottom() {
      return 50 + this.mainHeight
    },
    firstPull() {
      return this.talkList.findIndex(i => i.type === 0)
    }
  },
  onLoad() {
    /*#ifdef H5*/
    this.scrollView.safeAreaHeight = uni.getSystemInfoSync().safeArea.height;
    /* #endif*/

    /*#ifdef MP*/
    // 初始化调用
    this.initRecord();
    /*#endif*/

    this.getMessage();
  },
  methods: {
    scrollToBottom: function () {
      const query = uni.createSelectorQuery();
      query.select('#scroll').boundingClientRect((rect) => {
        if (rect) {
          this.scrollTop = rect.top + 300
        }
      });
      query.exec();
    },
    // 输入框高度改变
    linechange(e) {
      this.mainHeight = e.detail.height
    },
    handleVolume(index) {
      this.$set(this.talkList[index], 'isVolume', true)
      this.startTextToSpeech2(this.talkList[index].content, index)
    },
    handlePlay(item) {
      this.isPlay = true
      // this.startTextToSpeech1(item.content)
      // 创建 SpeechSynthesisUtterance 对象
      var speechUtterance = new SpeechSynthesisUtterance('Hello, how are you?');

      // 创建 SpeechSynthesis 对象
      var synthesis = window.speechSynthesis;

      // 设置语音合成的事件处理函数

      // 开始语音合成
      speechUtterance.onstart = function (event) {
        console.log('Speech synthesis started.');
      };

      // 结束语音合成
      speechUtterance.onend = function (event) {
        console.log('Speech synthesis ended.');
      };

      // 暂停语音合成
      speechUtterance.onpause = function (event) {
        console.log('Speech synthesis paused.');
      };

      // 恢复语音合成
      speechUtterance.onresume = function (event) {
        console.log('Speech synthesis resumed.');
      };

      // 分段语音合成
      speechUtterance.onboundary = function (event) {
        console.log('Speech boundary reached at character index ' + event.charIndex + '.');
      };

      // 启动语音合成
      var btn = document.querySelector('button');
      btn.addEventListener('click', function () {
        synthesis.speak(speechUtterance);
      });
    },
    cancelPlay() {
      this.isPlay = false
      innerAudioContext.stop()
    },
    startTextToSpeech1(text) {
      let that = this
      plugin.textToSpeech({
        lang: "zh_CN", // 语言代码，这里是中文
        content: text,
        tts: true,
        success: function (res) {
          console.log("文字转语音成功", res);
          innerAudioContext.pause();
          // 播放生成的语音
          innerAudioContext.src = res.filename;
          innerAudioContext.play();
          innerAudioContext.onEnded(() => {
            that.isPlay = false
          })
        },
        fail: function (err) {
          that.isPlay = false
          uni.showToast({
            title: '文字转语音失败',
            icon: 'none',
            duration: 3000
          })
          console.error("文字转语音失败", err);
        },
        finally: function () {
          that.isPlay = false
        }
      });
    },
    startTextToSpeech2(text, index) {
      let that = this
      plugin.textToSpeech({
        lang: "zh_CN", // 语言代码，这里是中文
        content: text,
        tts: true,
        success: function (res) {
          console.log("文字转语音成功", res);
          innerAudioContext.pause();
          // 播放生成的语音
          innerAudioContext.src = res.filename;
          innerAudioContext.play();
          innerAudioContext.onEnded(() => {
            console.log('结束播放', index);
            that.$set(that.talkList[index], 'isVolume', false)
          })
        },
        fail: function (err) {
          console.error("文字转语音失败", err);
        },
        finally: function () {
          this.$set(this.talkList[index], 'isVolume', false)
        }
      });
    },
    // 说话 开始
    startRecord() {
      // 先暂停音频播放（如果正在播放）
      innerAudioContext.pause();
      console.log('开始录音');
      manager.start({
        duration: 30000,
        lang: "zh_CN"
      });
    },
    endRecord() {
      console.log('录音结束');
      this.shows = true;
      this.stopRecordingAnimation()
      manager.stop().catch((err) => {
        console.log('停止录音出现错误:', err);
        this.resetVoice()
        this.initRecord()
      });
    },
    playVoice() {
      console.log('播放录音');
      if (this.voicePath) {
        innerAudioContext.src = this.voicePath;
        innerAudioContext.play();
      }
    },
    resetVoice() {
      console.log('重置录音');
      innerAudioContext.stop();
      this.shows = false
      this.voicePath = '';
      this.voiceText = ''
      this.num = 1
    },
    handleTouchStart(e) {
      this.startY = e.touches[0].pageY
      this.isCancelled = false
      this.showRecordPanel = true
      this.startRecording()
    },
    startRecordingAnimation() {
      const interval = setInterval(() => {
        if (!this.isRecording) {
          clearInterval(interval);
          return;
        }
        const randomHeights = [];
        for (let i = 0; i < 7; i++) {
          randomHeights.push(Math.random() * 20 + 10);
        }
        this.pillarHeights = randomHeights;
      }, 200);
    },
    stopRecordingAnimation() {
      this.pillarHeights = Array(7).fill(0);
    },
    handleKeyboard() {
      this.isMic = false
    },
    handleMessage() {
      this.showHotWord = true
    },
    contentBlur() {
      this.focusStatus = false
    },
    contentFocus() {
      this.focusStatus = true
    },
    // 下拉刷新
    refresherrefresh(e) {
      this.getMessage();
      this.scrollView.refresherTriggered = true;
    },
    // 获取历史消息
    getMessage() {
      if (!this.ajax.flag) {
        return;
      }

      // 此处用到 ES7 的 async/await 知识，为使代码更加优美。不懂的请自行学习。
      let get = async () => {
        this.ajax.flag = false;
        let data = await getHistoryMsg({
          page: this.ajax.page,
          rows: this.ajax.rows
        });
        this.scrollView.refresherTriggered = false;

        // 获取待滚动元素选择器，解决插入数据后，滚动条定位时使用。取当前消息数据的第一条信息元素
        const selector = `msg-${data?.[0]?.id}`;
        ;

        // 将获取到的消息数据合并到消息数组中
        this.talkList = this.talkList.concat(data);

        // 数据挂载后执行，不懂的请自行阅读 Vue.js 文档对 Vue.nextTick 函数说明。
        this.$nextTick(() => {
          // 设置当前滚动的位置
          this.scrollView.intoView = selector;
          if (this.ajax.page === 1) {
            this.scrollToBottom()
          }
          if (data.length < this.ajax.rows) {
            // 当前消息数据条数小于请求要求条数时，则无更多消息，不再允许请求。
            // 可在此处编写无更多消息数据时的逻辑
          } else {
            this.ajax.flag = true;
            this.ajax.page++;
          }

        })
      }
      get();
    },
    // 发送信息
    handleSendClick() {
      if (!this.content) {
        uni.showToast({
          title: '请输入有效的内容',
          icon: 'none'
        })
        return;
      }
      this.sendMessage(this.content)
      // 清空内容框中的内容
      this.content = '';
    },
    // 处理图片点击
    handleImageClick() {
      uni.chooseImage({
        count: 1, //默认9
        sizeType: ['original'],
        sourceType: ['album', 'camera'],
        success: (res) => {
          const imageUrl = res.tempFilePaths[0];
          this.sendMessage(imageUrl, 'image');
        }
      });
    },
    sendMessage(content, contentType = 'text') {
      // 将当前发送信息 添加到消息列表。
      let data = {
        "id": new Date().getTime(),
        content,
        contentType,
        "type": 1,
        "pic": "/static/logo.png"
      }
      this.talkList.unshift(data);
      this.$nextTick(() => {
        this.scrollToBottom()
      })
    },
    handleTouchMove(e) {
      if (!this.isRecording) return

      const currentY = e.touches[0].pageY
      const deltaY = this.startY - currentY

      if (deltaY > 100) {
        this.isCancelled = true
      } else {
        this.isCancelled = false
      }
    },

    async handleTouchEnd() {
      this.showRecordPanel = false
      clearInterval(this.timer)

      if (this.isCancelled) {
        this.handleCancelRecord()
        return
      }

      if (this.recordTime < 1) {
        this.showErrorTip('说话时间太短')
        return
      }

      try {
        await this.endRecord()
        if (this.voiceText === '') {
          this.showErrorTip('没有检测到语音内容')
        }
      } catch (error) {
        this.showErrorTip('识别失败，请重试')
      }
    },

    handleTouchCancel() {
      this.showRecordPanel = false
      this.handleCancelRecord()
    },

    // 开始录音
    async startRecording() {
      try {
        if (!await this.checkRecordPermission()) return

        this.isRecording = true
        this.recordTime = 0
        manager.start({
          duration: this.maxRecordTime * 1000,
          lang: "zh_CN"
        })

        this.timer = setInterval(() => {
          if (this.recordTime >= this.maxRecordTime) {
            this.handleTouchEnd()
            return
          }
          this.recordTime++
        }, 1000)
      } catch (err) {
        this.handleError(err)
      }
    },

    handleCancelRecord() {
      this.isRecording = false
      this.recordTime = 0
      manager.stop()
      this.resetVoice()
      this.showErrorTip('已取消发送')
    },

    // 增强错误处理
    showErrorTip(message) {
      this.errorMessage = message
      this.showError = true
      setTimeout(() => {
        this.showError = false
      }, 2000)
    },

    // 修改后的识别结果处理
    initRecord() {
      manager.onStart = () => {
        console.log('录音开始');
        this.isRecording = true;
      }
      manager.onError = (res) => {
        this.showErrorTip(this.getErrorMessage(res.errCode))
        this.resetRecordingState()
      }

      manager.onStop = (res) => {
        if (this.isCancelled) return

        this.voiceText = res.result
        // if (!this.voiceText) {
        //   this.showErrorTip('未识别到有效内容')
        //   return
        // }
        this.sendMessage(this.voiceText)
        this.resetVoice()
      }
    },
    // 错误处理统一方法
    handleError(error) {
      console.error('录音错误:', error)
      this.resetRecordingState()
      let msg = '录音失败'

      // 微信错误码处理
      if (error.errCode) {
        const errorMap = {
          10001: '系统错误',
          10002: '网络错误',
          10003: '录音权限未授权',
          10004: '录音时间太短',
          10005: '未识别到内容'
        }
        msg = errorMap[error.errCode] || msg
      }

      this.showErrorTip(msg)
    },

    // 状态重置方法
    resetRecordingState() {
      clearInterval(this.timer)
      this.isRecording = false
      this.recordTime = 0
      this.showRecordPanel = false
      this.isCancelled = false
    },

    //  录音权限检查
    checkRecordPermission: async function() {
      try {
        const res = await uni.getSetting()
        if (!res.authSetting['scope.record']) {
          await uni.authorize({ scope: 'scope.record' })
        }
        return true
      } catch (err) {
        this.handleError(err)
        return false
      }
    },

    // 错误码映射
    getErrorMessage(code) {
      const errors = {
        10001: '系统错误',
        10002: '网络错误',
        10003: '录音权限未授权',
        10004: '录音时间过短',
        10005: '识别内容为空',
        // 添加更多错误码...
      }
      return errors[code] || '识别失败，请重试'
    }
  }
}
</script>

<style lang="scss">
@import "../../lib/global.scss";

page {
  background-color: #F3F3F3;
  font-size: 28rpx;
}

.container {
  height: calc(100vh - var(--window-top));
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-content: center;
  justify-content: space-between;
  align-items: stretch;
  background-color: #fff;
}

/* 加载数据提示 */
.tips {
  position: fixed;
  left: 0;
  top: var(--window-top);
  width: 100%;
  z-index: 9;
  background-color: rgba(0, 0, 0, 0.15);
  height: 72rpx;
  line-height: 72rpx;
  transform: translateY(-80rpx);
  transition: transform 0.3s ease-in-out 0s;

  &.show {
    transform: translateY(0);
  }
}

.box-1 {
  width: 100%;
  height: 0;
  flex: 1 0 auto;
  box-sizing: content-box;
}

.footer {
  width: 92%;
  height: auto;
  z-index: 2;
  box-sizing: content-box;
  background-color: #fff;
  position: fixed;
  bottom: 0;
  margin-bottom: 50rpx;
  left: 50%;
  border-radius: 50rpx;
  transform: translateX(-50%);
  box-shadow: 0 3px 8px 2px #716ae51a;
  display: flex;
  align-items: center;

  /* 兼容iPhoneX */
  //padding-bottom: 0;
  padding-bottom: constant(safe-area-inset-bottom);
  // padding-bottom: env(safe-area-inset-bottom);

  > view {
    padding: 0 20rpx;
    // min-height: 100rpx;
  }

  .message {
    width: 80rpx;
    height: 80rpx;
    border-radius: 40rpx;
    overflow: hidden;
    box-shadow: 0 3px 8px 2px #716ae51a;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9;
    transition: width 0.15s, padding 0.15s;

    &._hide {
      width: 0;
      padding: 0;
    }

    &.send {
      box-shadow: none;
    }
  }

  .talk {
    flex: 1;
    text-align: center;
  }

  .content {
    background-color: #fff;
    // height: 64rpx;
    // padding: 0 20rpx;
    border-radius: 32rpx;
    font-size: 28rpx;
  }

  ::v-deep .u-textarea {
    border: none;
    padding: 0 0 0 20rpx !important;
    line-height: normal;

    textarea {
      max-height: 200rpx;
      overflow-y: scroll;
    }
  }

  ::v-deep .input-placeholder {
    pandding-top: 15rpx !important;
  }
}

.talk-list {
  padding-bottom: 20rpx;
  display: flex;
  flex-direction: column-reverse;
  flex-wrap: nowrap;
  align-content: flex-start;
  justify-content: flex-end;
  align-items: stretch;

  // 添加弹性容器，让内容自动在顶部
  &::before {
    content: '.';
    display: inline;
    visibility: hidden;
    line-height: 0;
    font-size: 0;
    flex: 1 0 auto;
    height: 1px;
  }


  /* 消息项，基础类 */
  .item {
    padding: 20rpx 20rpx 0 20rpx;
    align-items: flex-start;
    align-content: flex-start;
    padding: 24rpx 28rpx;
    gap: 10px;


    .pic {
      width: 92rpx;
      height: 92rpx;
      border-radius: 50%;
      border: #fff solid 1px;
    }

    .content {
      padding: 20rpx 30rpx;
      border-radius: 4px;
      max-width: 500rpx;
      word-break: break-all;
      line-height: 1.5;
      position: relative;

      .text {
        color: #fff;

      }

      .order-buts {
        margin-top: 14rpx;
        padding: 18rpx 0 4rpx;
        border-top: 3rpx solid #E5E5E5;
        color: $uni-color-primary;
        display: flex;
        justify-content: space-between;

        .buts-left,
        .buts-right {
          display: flex;
          gap: 20rpx;

          ._item {
            width: 50rpx;
            height: 50rpx;
            display: flex;
            justify-content: center;
            align-items: center;
            border-radius: 8rpx;
            background: #e4e6f5;
            color: $uni-color-primary;
          }
        }
      }
    }

    ._btns {
      width: 100%;
      padding: 12rpx;
      display: flex;
      gap: 12rpx;
    }

    /* 收到的消息 */
    &.pull {
      .content {
        border-radius: 10px 10px 10px 0;
        background-color: #F2F4F7;

        .text {
          color: #000000;
        }

        // margin-left: 32rpx;
        // &::after {
        // 	content: '';
        // 	display: block;
        // 	width: 0;
        // 	height: 0;
        // 	border-top: 16rpx solid transparent;
        // 	border-bottom: 16rpx solid transparent;
        // 	border-right: 20rpx solid #fff;
        // 	position: absolute;
        // 	top: 30rpx;
        // 	left: -18rpx;
        // }
      }
    }

    /* 发出的消息 */
    &.push {
      /* 主轴为水平方向，起点在右端。使不修改DOM结构，也能改变元素排列顺序 */
      flex-direction: row-reverse;

      .content {
        background-color: $uni-color-primary;
        border-radius: 10px 10px 0 10px;

        // margin-right: 32rpx;
        // &::after {
        // 	content: '';
        // 	display: block;
        // 	width: 0;
        // 	height: 0;
        // 	border-top: 16rpx solid transparent;
        // 	border-bottom: 16rpx solid transparent;
        // 	border-left: 20rpx solid #a0e959;
        // 	position: absolute;
        // 	top: 30rpx;
        // 	right: -18rpx;
        // }
      }

      ._btns {
        justify-content: flex-end;
      }
    }
  }
}

.talk-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  position: relative;

  .voiceText {
    position: absolute;
    left: 0;
    top: -300rpx;
    width: 100%;
    text-align: center;
  }
}

.talk {
  width: 100%;
  font-size: 16px;
  color: #333;
}

.pillars {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 20rpx;
}

.pillar {
  width: 14rpx;
  background-color: $uni-color-primary;
  border-radius: 6rpx;
}

.voice-playing {
  height: 100%;
  width: 100%;
  display: flex;
  gap: 6rpx;
  /* 底部对齐，实现从下往上的动画效果 */
  align-items: center;
  justify-content: center;
}

.play1, .play2, .play3 {
  width: 6rpx;
  border-radius: 4rpx;
  background-color: $uni-color-primary;
}

.play1 {

  animation: playing1 .6s linear infinite alternate;
}

.play2 {
  animation: playing2 .6s linear infinite alternate;
}

.play3 {
  animation: playing3 .6s linear infinite alternate;
}

@keyframes playing1 {
  0% {
    height: 15%;
  }

  100% {
    height: 40%;
  }
}

@keyframes playing2 {
  0% {
    height: 40%;
  }

  100% {
    height: 15%;
  }
}

@keyframes playing3 {
  0% {
    height: 15%;
  }

  100% {
    height: 40%;
  }
}

.record-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 999;

  .record-content {
    background: rgba(255, 255, 255, 0.1);
    padding: 40rpx 60rpx;
    border-radius: 40rpx;
    text-align: center;
  }

  .record-time {
    color: #fff;
    font-size: 48rpx;
    margin-bottom: 30rpx;
  }

  .record-animation {
    position: relative;
    width: 160rpx;
    height: 160rpx;
    margin: 0 auto 30rpx;

    .wave {
      position: absolute;
      width: 100%;
      height: 100%;
      border: 6rpx solid #fff;
      border-radius: 50%;
      animation: wavePulse 1.5s infinite;
    }
  }

  .cancel-tip {
    color: #fff;
    font-size: 28rpx;
    transition: all 0.3s;

    &.active {
      color: #ff4444;
    }
  }

  .cancel-icon {
    margin-top: 20rpx;
  }
}

@keyframes wavePulse {
  0% { transform: scale(1); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* 优化现有样式 */
.talk-container {
  position: relative;

  .voiceText {
    min-height: 80rpx;
    padding: 20rpx;
    text-align: center;

    .error-tip {
      color: #ff4444;
      font-size: 24rpx;
      margin-top: 10rpx;
    }
  }

  .talk {
    background: $uni-color-primary;
    color: #fff;
    border-radius: 40rpx;
    padding: 20rpx 40rpx;
    transition: all 0.3s;

    &.recording {
      background: #ff4444;
      transform: scale(1.1);
    }
  }
}

/* 优化波形动画 */
.pillar {
  transition: height 0.2s ease-in-out;
}
</style>